在寫這一篇的時候,
看到右邊 iT邦幫忙鐵人賽 跳出的話,
可能你沒有他人的天賦,但你沒有理由比別人不努力。
是啊,正因為我不是天才,所以才更應該要努力不是嗎?
(雖然我自己知道我很不認真就是了XD")
今天要主攻戰鬥選單,
大家應該會注意到,
當該選項被選擇的時候,
是會顯示土黃色的。
那要怎麼做呢?
總不能將被選擇的樣子截圖下來,
用圖片交換吧?
這樣就太 low 了XD
對,所以我們應該是該選項被選到的時候在其上面疊上土黃色色塊(遮罩?)才對。
然後選項看起來是正八邊形,
所以我們要試圖弄出正八邊形才行。
話說我相信這是高中數學的程度,
但小的高中數學奇差無比,
然後去年遇到這個問題的時候,
想說「欠的果然都是要還的」orz
那時候沒學好的高中數學果然還是回來找我了啊啊啊啊啊~~~~~(吶喊)
為了這個我甚至跑去買高中數學的三角函數來 K,
(因為好像邊長、角度的關係公式是三角函數的東西?)
反正到最後我其實還是沒搞懂三角函數orz
我到 codepen, Google 上找尋各位大大用正X邊形是怎麼弄出來的,
然後再自己依據比例原則 tune,
終於 tune 出像樣的東西orz
因此這邊原諒小的不說明這邊的 CSS,因為老實說我也不太知道怎麼用出來的QQ
這邊直接提供我的做法,
html 要加上 mask 的物件:
<div class="option skill active">
<div class="mask"></div>
</div>
CSS 看起來會用到偽元素:
.active{
.mask{
position:relative;
opacity: 0.35;
width:95px;
height:0;
border-width:0 25px 25px 25px;
border-style:solid;
border-color:transparent transparent #846c4f;
&:before{
position:absolute;
content:"";
top:70px;
left:-25px;
width:95px;
height:0;
border-width:25px 25px 0 25px;
border-style:solid;
border-color: #846c4f transparent transparent;
}
&:after{
position:absolute;
content:"";
top:25px;
left:-25px;
width:95px;
height:0;
border-width:0 0 45px 0;
background:none;
border-style:solid;
border-color:transparent transparent #846c4f;
}
}
}
讓我們看看效果吧!
唷,看起來很棒!
去年的我是被這篇所救 → 單一 div 的正多邊形變換 ( 純 CSS ) | OXXO.STUDIO
有大大的詳細講解,
小的在這邊跟大大鞠躬道謝 m(_ _)m
上面弄出我們要的樣子後,
接下來就是要判斷,被選到的選項才要加土黃色遮罩,
而且預設選項就是普通攻擊。
這邊我們先試一個東西,
就是控制物件的 class 要加上 active,
因為我們的 CSS 是將 mask 訂在 active 底下,
因此 mask 的父容器 class 有 active,
mask 才會顯示出來。
html 結構是這樣:
<div class="war_menu">
<div class="option skill">
<div class="mask"></div>
</div>
<div class="center">
<div class="option attack">
<div class="mask"></div>
</div>
<div class="option item">
<div class="mask"></div>
</div>
</div>
<div class="option cooperation">
<div class="mask"></div>
</div>
</div>
這邊我先在 war_menu 的物件埋一個 data-option 的屬性,
來存放現在選到的選項是什麼,
再來 JavaScript 就可以用 物件.dataset.option 來取得 data-option 裡面的值。
html
<div class="war_menu" data-option="skill"></div>
JavaScript
const warMenuElement = document.querySelector(".war_menu");
console.log(`現在的選項是:${warMenuElement.dataset.option}`);
然後因為之後 data-option 要隨著不同的選項被選到,值的內容也要跟著變動,
所以這邊也試一下可不可以改 data-option 的值。
warMenuElement.setAttribute("data-option","cooperation");
console.log(`現在的選項是:${warMenuElement.dataset.option}`);
看起來也 OK!
我們的最終目標是,拿 data-option 的值 比對 class,
假設 data-option 的值為 cooperation,
比對戰鬥選單的 4 個選項的 class,
如果有比對到 cooperation,
就在它的 class 後面加上 active。
因此這邊就要用到正規表示式了,
這邊用的語法是 patten.test("要比對的字串"),
回傳 true/false 這樣。
還有因為選項有 4 個,不想寫 4 行 document.querySelector,
可以使用 document.querySelectorAll,它會將相同 class 的物件都抓出來,成為陣列。
再來獲得屬性的語法是 物件.getAttribute("屬性名稱"),
設定屬性的語法是 物件.setAttribute("屬性名稱","值")。
// 正規表示式的 Pattern 設為現在被選到的選項
let pattCurrentOption = new RegExp(warMenuElement.dataset.option);
const warMenuOptionElement = document.querySelectorAll(".war_menu .option");
for ( let i=0; i<warMenuOptionElement.length; i++){
console.log(`現在的 class 是:${warMenuOptionElement[i].getAttribute("class")}`);
let result = pattCurrentOption.test(warMenuOptionElement[i].getAttribute("class"));
console.log(`比對結果:${result}`);
// 如果有比對到的,該物件 class 加上 active
if ( result ){
warMenuOptionElement[i].setAttribute("class", `${warMenuOptionElement[i].getAttribute("class")} active`);
}
}
前面我們設定 data-option 的值為 cooperation,
因此最後我們可以看到 cooperation 有土黃色遮罩了!
再來還有一件事要搞定,
要讓玩家可以自行選選項,
按照原版方式是要以按鍵盤方式,
所以要偵測鍵盤事件,
而且要偵測上下左右鍵。
鍵盤事件不是針對單一物件的監聽,
所以要以整個視窗為監聽對象。
window.addEventListener("keydown", optionSelect);
function optionSelect(event){
console.log(`keyCode: ${event.keyCode}`);
}
keyCode 就是按下哪個鍵的數值,
但你會說我怎麼知道數值是對應到哪個鍵呢?
JavaScript Event KeyCodes
這個網站有按鍵跟值的對應,
像我按下 Ctrl 鍵就會顯示其對應的 keyCode 為 17。
再來我們剖析一下停在每個選項所可以按的鍵:
然後我們用 switch case 判斷現在所按下的鍵,
比如說點擊往下鍵,
攻擊就要有土黃色遮罩。
window.addEventListener("keydown", optionSelect);
function optionSelect(event){
console.log(`keyCode: ${event.keyCode}`);
let currentOption = warMenuElement.dataset.option;
switch(event.keyCode){
case 38:
console.log("往上鍵 有被點擊到");
console.log(`currentOption: ${currentOption}`);
if ( currentOption === "skill" ){
warMenuElement.setAttribute("data-option","attack");
setActiveClass();
}
break;
default:
console.log("default");
break;
}
}
看來是有了,
但原本選項的 active 要移掉囧
這次用正規表示式的 replace 語法來試試。
for ( let i=0; i<warMenuOptionElement.length; i++){
let optionClass = warMenuOptionElement[i].getAttribute("class");
let result = pattCurrentOption.test(warMenuOptionElement[i].getAttribute("class"));
// 如果有比對到的,該物件 class 加上 active
// 沒有的話,該物件 class 移除 active
if ( result ){
warMenuOptionElement[i].setAttribute("class", `${optionClass} active`);
} else{
optionClass = optionClass.replace("active","");
warMenuOptionElement[i].setAttribute("class",optionClass);
}
}
看來成功了!
(PS. 這時候我就很懷念 jQuery 的 toggleClass 功能,
物件.toggleClass("active") => 它會自己去看該物件的 class 有沒有 active,
有就移除 active,沒有就加上 active,
超方便好用,
但想說這次就不要用到 jQuery 了,用原生語法拼出來QQ)
好,中間支線解決了,
再來就是將每個鍵盤事件都定義好。
window.addEventListener("keydown", optionSelect);
function optionSelect(event){
switch(event.keyCode){
case 38: // 上鍵
warMenuElement.setAttribute("data-option","attack");
break;
case 40: // 下鍵
warMenuElement.setAttribute("data-option","item");
break;
case 37: // 左鍵
warMenuElement.setAttribute("data-option","skill");
break;
case 39: // 右鍵
warMenuElement.setAttribute("data-option","cooperation");
break;
default:
console.log("default");
break;
}
setActiveClass(); // 是現在所點的選項就加上土黃色遮罩
}
看來很棒,根據我點選上下左右土黃色遮罩也會跟著移動。
但是其實有一個 bug,
就是如果我現在向下鍵故意按 2 次,
再點選其他鍵:
就會看到原本的選項土黃色遮罩沒有被移除掉,
看一下 console.log:
原因是因為原本選項是 item,
但我又點選向下鍵 2 次,
item 的 class 被疊加 2 次 active,
所以我的做法是在加 active 之前先看看 class 是否已包含 active,
沒有的話才加上 active。
let pattActiveOption = new RegExp("active","g");
if ( result ){
if ( !pattActiveOption.test(optionClass) ){ // class 沒有 active 才能加 active
warMenuOptionElement[i].setAttribute("class", `${optionClass} active`);
}
}
看來正常了!
沒想到用戰鬥選單花這麼多時間XD
那本日收工XD
明天應該就要進入回合制的設定,
真正的硬仗orz
但先看一下這三天的成果,
BGM 再自己配上戰鬥曲→ 仙劍奇俠傳 - 風起雲湧 - Battle Theme - BGM
整個超有 fu!
頑張!!!!!